package xworker.netty.handlers;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelPromise;
import org.xmeta.ActionContext;
import org.xmeta.Thing;
import org.xmeta.util.UtilData;
import xworker.lang.executor.Executor;

import java.net.SocketAddress;

public class ThingChannelOutboundHandler implements ChannelOutboundHandler {
    private static final String TAG = ThingChannelOutboundHandler.class.getName();

    Thing thing;
    ActionContext actionContext;

    public ThingChannelOutboundHandler(Thing thing, ActionContext actionContext) {
        this.thing = thing;
        this.actionContext = actionContext;
    }

    @Override
    public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
        thing.doAction("bind", actionContext, "ctx", ctx, "localAddress", localAddress, "promise", promise);
    }

    public static void handleEvent(Thing self, String name, ActionContext actionContext) {
        if(UtilData.isTrue(self.doAction("isPrintLog", actionContext))) {
            ChannelHandlerContext ctx = actionContext.getObject("ctx");
            Executor.info(TAG, self.getMetadata().getPath() + ":" + name + ": " + ctx);
        }

    }

    public static void bind(ActionContext actionContext){
        Thing self = actionContext.getObject("self");
        handleEvent(self, "bind", actionContext);
    }

    @Override
    public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
        thing.doAction("connect", actionContext, "ctx", ctx, "remoteAddress", remoteAddress,  "localAddress", localAddress, "promise", promise);
    }

    public static void connect(ActionContext actionContext){
        Thing self = actionContext.getObject("self");
        ChannelHandlerContext ctx = actionContext.getObject("ctx");
        SocketAddress remoteAddress = actionContext.getObject("remoteAddress");
        SocketAddress localAddress = actionContext.getObject("localAddress");
        ChannelPromise promise = actionContext.getObject("promise");
        ctx.connect(remoteAddress, localAddress, promise);

        handleEvent(self, "connect", actionContext);
    }

    @Override
    public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        thing.doAction("disconnect", actionContext, "ctx", ctx, "promise", promise);
    }

    public static void disconnect(ActionContext actionContext){
        Thing self = actionContext.getObject("self");
        ChannelHandlerContext ctx = actionContext.getObject("ctx");
        ChannelPromise promise = actionContext.getObject("promise");
        ctx.disconnect(promise);

        handleEvent(self, "disconnect", actionContext);
    }

    @Override
    public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        thing.doAction("close", actionContext, "ctx", ctx, "promise", promise);
    }

    public static void close(ActionContext actionContext){
        Thing self = actionContext.getObject("self");

        ChannelHandlerContext ctx = actionContext.getObject("ctx");
        ChannelPromise promise = actionContext.getObject("promise");
        ctx.close(promise);

        handleEvent(self, "close", actionContext);
    }

    @Override
    public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        thing.doAction("deregister", actionContext, "ctx", ctx, "promise", promise);
    }

    public static void deregister(ActionContext actionContext){
        Thing self = actionContext.getObject("self");

        ChannelHandlerContext ctx = actionContext.getObject("ctx");
        ChannelPromise promise = actionContext.getObject("promise");
        ctx.deregister(promise);

        handleEvent(self, "deregister", actionContext);
    }

    @Override
    public void read(ChannelHandlerContext ctx) throws Exception {
        thing.doAction("read", actionContext, "ctx", ctx);
    }

    public static void read(ActionContext actionContext){
        Thing self = actionContext.getObject("self");

        ChannelHandlerContext ctx = actionContext.getObject("ctx");
        ctx.read();

        handleEvent(self, "read", actionContext);
    }

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        thing.doAction("write", actionContext, "ctx", ctx, "msg", msg, "promise", promise);
    }

    public static void write(ActionContext actionContext){
        Thing self = actionContext.getObject("self");

        ChannelHandlerContext ctx = actionContext.getObject("ctx");
        Object msg = actionContext.getObject("msg");
        ChannelPromise promise = actionContext.getObject("promise");
        ctx.write(msg, promise);

        handleEvent(self, "write", actionContext);
    }

    @Override
    public void flush(ChannelHandlerContext ctx) throws Exception {
        thing.doAction("flush", actionContext, "ctx", ctx);
    }

    public static void flush(ActionContext actionContext){
        Thing self = actionContext.getObject("self");

        ChannelHandlerContext ctx = actionContext.getObject("ctx");
        ctx.flush();

        handleEvent(self, "flush", actionContext);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        thing.doAction("handlerAdded", actionContext, "ctx", ctx);
    }

    public static void handlerAdded(ActionContext actionContext){
        Thing self = actionContext.getObject("self");

        handleEvent(self, "handlerAdded", actionContext);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        thing.doAction("handlerRemoved", actionContext, "ctx", ctx);
    }

    public static void handlerRemoved(ActionContext actionContext){
        Thing self = actionContext.getObject("self");
        handleEvent(self, "handlerRemoved", actionContext);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        thing.doAction("exceptionCaught", actionContext, "ctx", ctx, "cause", cause);
    }

    public static void exceptionCaught(ActionContext actionContext){
        Thing self = actionContext.getObject("self");
        handleEvent(self, "exceptionCaught", actionContext);
    }

    public static ThingChannelOutboundHandler create(ActionContext actionContext) {
        Thing self = actionContext.getObject("self");

        ThingChannelOutboundHandler handler = new ThingChannelOutboundHandler(self, actionContext);
        actionContext.g().put(self.getMetadata().getName(), handler);

        return handler;
    }
}
